import logging
from flask import Response
import urllib.parse
from p_harmony.pub.param.page_info import PageInfo

logger = logging.getLogger(__name__)

from flask import Blueprint, request, jsonify, send_file
import tempfile
import os
from openpyxl import Workbook

from p_harmony.modules.prd.services.prd_func_dict_service import PrdFuncDictService
from p_harmony.database import get_db
from p_harmony.exceptions import APIException
from p_harmony.utils.decorators import api_response

from p_harmony.utils.model_util import ModelUtil
from p_harmony.modules.prd.models.prd_func_dict import PrdFuncDict
from p_harmony.utils.session_util import SessionUtil

prd_func_dict_blueprint = Blueprint('prd_func_dict', __name__)

@prd_func_dict_blueprint.route('/prd_func_dict', methods=['POST'])
@api_response
def create_prd_func_dict():
    data = request.get_json()
    model = ModelUtil.convert_request_data_to_model(PrdFuncDict, data)
    model.createNo = SessionUtil.get_current_user()["userNo"]
    model.createName = SessionUtil.get_current_user()["userName"]
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.create(model)

@prd_func_dict_blueprint.route('/prd_func_dict/<id>', methods=['GET'])
@api_response
def get_prd_func_dict(id):
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.get_by_id(id)

@prd_func_dict_blueprint.route('/prd_func_dict/<id>', methods=['PUT'])
@api_response
def update_prd_func_dict(id):
    data = request.get_json()
    model = ModelUtil.convert_request_data_to_model(PrdFuncDict, data)
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.update(id, model)

@prd_func_dict_blueprint.route('/prd_func_dict/<id>', methods=['DELETE'])
@api_response
def delete_prd_func_dict(id):
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.delete(id)

@prd_func_dict_blueprint.route('/prd_func_dict', methods=['GET'])
@api_response
def get_all_prd_func_dict():
    page = int(request.args.get('page', 1))
    limit = int(request.args.get('limit', 10))
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.get_all(page, limit)

@prd_func_dict_blueprint.route('/prd_func_dict/page', methods=['GET'])
@api_response
def get_page_prd_func_dict():
    page = int(request.args.get('page', 1))
    limit = int(request.args.get('limit', 10))
    sort = request.args.get('sort')
    filters = {}
    for key, value in request.args.items():
        if key not in ['page', 'limit', 'sort']:
            filters[key] = value
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    return service.get_page(page, limit, filters or None, sort)

# 删除@api_response装饰器，因为send_file返回的是Response对象而不是JSON数据
@prd_func_dict_blueprint.route('/prd_func_dict/export', methods=['GET'])
def export_prd_func_dict():
    '''
    导出数据方法
    1. 按条件分页查询 2. 写入临时excel文件，直到查询不到数据 3. 以IO的方式返回excel数据 4. 清理临时excel文件
    '''
    # 1. 获取查询参数
    page = 1  # 从第一页开始导出
    limit = 1000  # 每页1000条数据
    sort = request.args.get('sort')
    filters = {}
    for key, value in request.args.items():
        if key not in ['page', 'limit', 'sort']:
            filters[key] = value
            
    # 2. 初始化数据库连接和服务
    db_generator = get_db()
    db = next(db_generator)
    service = PrdFuncDictService(db)
    
    # 3. 创建临时Excel文件
    temp_file = None
    try:
        # 创建临时文件
        temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx')
        temp_file.close()
        
        # 创建工作簿
        wb = Workbook()
        ws = wb.active
        ws.title = "产品功能字典数据"
        
        # 4. 初始化变量
        is_first_batch = True  # 标记是否是第一批次数据（用于写入表头）
        column_titles = []     # 存储列标题
        
        # 5. 分页查询并写入Excel
        while True:
            # 分页获取数据
            page_result = service.get_page(page, limit, filters or None, sort)
            # 确保正确访问分页数据
            if hasattr(page_result, 'data'):
                page_info = page_result.data
            else:
                page_info = page_result
            
            # 检查是否有数据
            if not page_info or not hasattr(page_info, 'items') or not page_info.items:
                break
                
            # 如果是第一批次数据，写入表头
            if is_first_batch and page_info.items:
                # 获取第一个对象的属性作为表头
                first_item = page_info.items[0]
                # 正确处理模型对象以获取列标题
                if hasattr(first_item, 'to_dict'):
                    column_titles = list(first_item.to_dict().keys())
                elif hasattr(first_item, '__dict__'):
                    column_titles = list(first_item.__dict__.keys())
                else:
                    column_titles = list(vars(first_item).keys())
                    
                # 写入表头
                ws.append(column_titles)
                is_first_batch = False
            
            # 写入数据行
            for item in page_info.items:
                # 正确处理模型对象以获取数据行
                if hasattr(item, 'to_dict'):
                    row_data = [item.to_dict().get(col, '') for col in column_titles]
                elif hasattr(item, '__dict__'):
                    row_data = [getattr(item, col, '') for col in column_titles]
                else:
                    row_data = [vars(item).get(col, '') for col in column_titles]
                ws.append(row_data)
            
            # 如果当前页数据少于limit，说明已经是最后一页
            if len(page_info.items) < limit:
                break
                
            page += 1
        
        # 保存Excel文件
        wb.save(temp_file.name)

        print("outfile",temp_file.name)
        
        # 6. 使用流式方式返回文件，避免大文件导致内存溢出


        # 对文件名进行URL编码以避免HTTP头部编码问题
        filename = '产品功能字典数据.xlsx'
        encoded_filename = urllib.parse.quote(filename)
        
        def generate():
            try:
                with open(temp_file.name, 'rb') as f:
                    while True:
                        chunk = f.read(8192)  # 每次读取8KB
                        if not chunk:
                            break
                        yield chunk
            finally:
                # 确保文件在传输完成后被删除
                if os.path.exists(temp_file.name):
                    try:
                        os.unlink(temp_file.name)
                    except Exception as e:
                        logger.error(f"删除临时文件失败: {str(e)}")
        
        response = Response(
            generate(),
            mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            headers={
                'Content-Disposition': f'attachment; filename*=UTF-8\'\'{encoded_filename}',
                'Cache-Control': 'no-cache, no-store, must-revalidate',
                'Pragma': 'no-cache',
                'Expires': '0'
            }
        )
        # 明确设置HTTP状态码为200
        response.status_code = 200
        return response
        
    except Exception as e:
        logger.error(f"导出产品功能字典数据失败: {str(e)}")
        # 确保在异常情况下也删除临时文件
        if temp_file and os.path.exists(temp_file.name):
            try:
                os.unlink(temp_file.name)
            except Exception as e:
                logger.error(f"删除临时文件失败: {str(e)}")
        # 抛出带明确状态码的异常
        raise APIException("导出失败", status_code=500)
